﻿#include "myopenglwidget.h"
#include <QTime>
#include <QImage>
#include <QMatrix4x4>
#include <QVector3D>
#include <QKeyEvent>
#include <QApplication>

MyOpenGLWidget::MyOpenGLWidget()
{
    //设置上下文属性
    QSurfaceFormat format;
    format.setDepthBufferSize(24);
    format.setStencilBufferSize(8);
    format.setVersion(3, 3); //OpenGL 版本号3.3
    format.setProfile(QSurfaceFormat::CoreProfile);// 核心模式
    this->setFormat(format);
    //开启键盘事件捕捉
    this->grabKeyboard();
    //开启捕捉鼠标事件
    this->grabMouse();
    // 启用鼠标追踪
    this->setMouseTracking(true);
 }

MyOpenGLWidget::~MyOpenGLWidget()
{
    // Make sure the context is current and then explicitly
    // destroy all underlying OpenGL resources.
    makeCurrent();

    _vbo->destroy();
    _vao->destroy();
    _ebo->destroy();
    _texture->destroy();

    delete _texture;
    delete _texture1;
    delete _shaderProgram;
    delete _vao;
    delete _vbo;
    delete _ebo;

    doneCurrent();
}

void MyOpenGLWidget::initializeGL()
{
    //绑定OpenGL函数指针？类似GLAD库的作用？
    initializeOpenGLFunctions();
    //开启深度测试
    glEnable(GL_DEPTH_TEST);

    _shaderProgram = new QOpenGLShaderProgram;
    _shaderProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/basic_lighting.vert");
    _shaderProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/basic_lighting.frag");
    //链接
    if(!_shaderProgram->link()){
        qDebug() << "link failed!\n";
    }

    _shaderProgram1 = new QOpenGLShaderProgram;
    _shaderProgram1->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/light_cube.vert");
    _shaderProgram1->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/light_cube.frag");
    //链接
    if(!_shaderProgram1->link()){
        qDebug() << "link failed!\n";
    }

    float vertices[] = {
        -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
        0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
        0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
        0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
        -0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,

        -0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
        0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
        0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
        0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
        -0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,

        -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f,  0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f, -0.5f,  0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,

        0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,
        0.5f,  0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
        0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
        0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
        0.5f, -0.5f,  0.5f,  1.0f,  0.0f,  0.0f,
        0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,

        -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,
        0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,
        0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
        0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,

        -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,
        0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,
        0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
        0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
        -0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
        -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f
    };

    unsigned int indices[] = {
        0, 1, 3, // first triangle
        1, 2, 3  // second triangle
    };

    _vao = new QOpenGLVertexArrayObject();
    _vao->create();
    _vao->bind();

    _vbo = new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
    _vbo->create();
    if(!_vbo->bind()){
        qDebug() << "vbo绑定失败！";
    }
    _vbo->allocate(vertices, sizeof(vertices));

    _ebo = new QOpenGLBuffer(QOpenGLBuffer::IndexBuffer);
    _ebo->create();
    if(!_ebo->bind()){
        qDebug() << "ebo绑定失败！";
    }
    _ebo->allocate(indices, sizeof(indices));

    // position attribute
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    // normal attribute
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);

    _vao1 = new QOpenGLVertexArrayObject();
    _vao1->create();
    _vao1->bind();
    if(!_vbo->bind()){
        qDebug() << "vbo绑定失败！";
    }
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);

    //创建对象
    // _texture = new QOpenGLTexture(QImage(":/container.jpg").mirrored());
    // _texture->setWrapMode(QOpenGLTexture::Repeat);
    // _texture->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear);
    // _texture->setMagnificationFilter(QOpenGLTexture::Linear);
    // _texture->generateMipMaps();

    // _texture1 = new QOpenGLTexture(QImage(":/awesomeface.png").mirrored());
    // _texture1->setWrapMode(QOpenGLTexture::Repeat);
    // _texture1->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear);
    // _texture1->setMagnificationFilter(QOpenGLTexture::Linear);
    // _texture1->generateMipMaps();

}

void MyOpenGLWidget::paintGL()
{
    //update();
    //由于继承了QOpenGLFunctions，可以直接使用OpenGL中的函数
    // render
    // ------
    glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);//清除颜色和深度缓冲

    // bind Texture
    // glActiveTexture(GL_TEXTURE0);
    // _texture->bind();
    // glActiveTexture(GL_TEXTURE1);
    // _texture1->bind();

    //更新帧时间,由于定时器60fps，所以这里的_deltaTime为17上下
    _deltaTime = (QTime::currentTime().msecsSinceStartOfDay() - _lastMescCount) /1000.0f;
    _lastMescCount = QTime::currentTime().msecsSinceStartOfDay();

    _shaderProgram->bind();
    _shaderProgram->setUniformValue("objectColor", 1.0f, 0.5f, 0.31f);
    _shaderProgram->setUniformValue("lightColor", 1.0f, 1.0f, 1.0f);
    _shaderProgram->setUniformValue("lightPos", 1.2f, 1.0f, 2.0f);
    _shaderProgram->setUniformValue("viewPos", _cameraPos);
    QMatrix4x4 projection;
    QMatrix4x4 view;
    view.lookAt(_cameraPos, _cameraPos + _cameraFront, _cameraUp);
    projection.perspective(45.0f,
                           float(this->width())/float(this->height()),
                           0.1f,
                           100.0f);
    _shaderProgram->setUniformValue("view", view);
    _shaderProgram->setUniformValue("projection", projection);
    QMatrix4x4 model;
    _shaderProgram->setUniformValue("model", model);
    _vao->bind();
    glDrawArrays(GL_TRIANGLES, 0, 36);

    _shaderProgram1->bind();
    _shaderProgram1->setUniformValue("view", view);
    _shaderProgram1->setUniformValue("projection", projection);
    model.translate(1.2f, 1.0f, 2.0f);
    model.scale(0.2);
    _shaderProgram1->setUniformValue("model", model);
    _vao1->bind();
    glDrawArrays(GL_TRIANGLES, 0, 36);
}

void MyOpenGLWidget::resizeGL(int width, int height) {
    // OpenGL渲染窗口的尺寸大小，glViewport可以设置位置和宽高
    Q_UNUSED(width);
    Q_UNUSED(height);

    const qreal retinaScale = devicePixelRatio();
    glViewport(0, 0, this->width() * retinaScale, this->height() * retinaScale);//视口的宽度和高度将根据设备的像素比例进行缩放
    _lastMousePos = rect().center();
}

void MyOpenGLWidget::keyPressEvent(QKeyEvent *event){
    float moveSpeed = _moveSpeed * _deltaTime;
    if(event->key() == Qt::Key_W){
        _cameraPos += moveSpeed * _cameraFront;
    }
    if(event->key() == Qt::Key_S){
        _cameraPos -= moveSpeed * _cameraFront;
    }
    if(event->key() == Qt::Key_A){
        _cameraPos -= QVector3D::crossProduct(_cameraFront, _cameraUp).normalized() * moveSpeed;
    }
    if(event->key() == Qt::Key_D){
        _cameraPos += QVector3D::crossProduct(_cameraFront, _cameraUp).normalized() * moveSpeed;
    }
    //执行默认事件，向下传递
    QOpenGLWidget::keyPressEvent(event);
}

void MyOpenGLWidget::mouseMoveEvent(QMouseEvent *event){
    float xOffset = event->pos().x() - _lastMousePos.x();
    float yOffset =  - event->pos().y() + _lastMousePos.y();
    // 获取当前widget的中心点坐标

    // 将鼠标设置到中心点
    QCursor::setPos(mapToGlobal(_lastMousePos));

    xOffset *= _sensitivity;
    yOffset *= _sensitivity;

    _yaw += xOffset;
    _pitch += yOffset;

    // make sure that when pitch is out of bounds, screen doesn't get flipped
    if (_pitch > 89.0f)
        _pitch = 89.0f;
    if (_pitch < -89.0f)
        _pitch = -89.0f;

    _cameraFront.setX(cos(qDegreesToRadians(_yaw)) * cos(qDegreesToRadians(_pitch)));
    _cameraFront.setY(sin(qDegreesToRadians(_pitch)));
    _cameraFront.setZ(sin(qDegreesToRadians(_yaw)) * cos(qDegreesToRadians(_pitch)));
    _cameraFront.normalize();
}


